#pragma once
#include "mymath.h"

struct Vector3 {
	JINGDU x, y, z;
	__host__ __device__ explicit Vector3(JINGDU xyz = 0.0f) noexcept : Vector3(xyz, xyz, xyz) {}
	__host__ __device__  Vector3(JINGDU x, JINGDU y, JINGDU z) noexcept : x(x), y(y), z(z) {}
	Vector3(const Vector3& v) noexcept = default;
	Vector3(Vector3&& v) noexcept = default;
	~Vector3() = default;

	Vector3& operator=(const Vector3& v) = default;
	Vector3& operator=(Vector3 && v) = default;

	__device__ bool HasNaNs() const {
		return std::isnan(x) || std::isnan(y) || std::isnan(z);
	}

	__device__ const Vector3 operator-() const {
		return { -x, -y, -z };
	}

	__device__ const Vector3 operator+(const Vector3& v) const {
		return { x + v.x, y + v.y, z + v.z };
	}
	__device__ const Vector3 operator-(const Vector3& v) const {
		return { x - v.x, y - v.y, z - v.z };
	}
	__device__ const Vector3 operator*(const Vector3& v) const {
		return { x * v.x, y * v.y, z * v.z };
	}
	__device__ const Vector3 operator/(const Vector3& v) const {
		return { x / v.x, y / v.y, z / v.z };
	}
	__device__ const Vector3 operator+(JINGDU a) const {
		return { x + a, y + a, z + a };
	}
	__device__ const Vector3 operator-(JINGDU a) const {
		return { x - a, y - a, z - a };
	}
	__device__ const Vector3 operator*(JINGDU a) const {
		return { x * a, y * a, z * a };
	}
	__device__ const Vector3 operator/(JINGDU a) const {
		const JINGDU inv_a = 1.0 / a;
		return { x * inv_a, y * inv_a, z * inv_a };
	}

	__device__ Vector3& operator+=(const Vector3& v) {
		x += v.x;
		y += v.y;
		z += v.z;
		return *this;
	}
	__device__ Vector3& operator-=(const Vector3& v) {
		x -= v.x;
		y -= v.y;
		z -= v.z;
		return *this;
	}
	__device__ Vector3& operator*=(const Vector3& v) {
		x *= v.x;
		y *= v.y;
		z *= v.z;
		return *this;
	}
	__device__ Vector3& operator/=(const Vector3& v) {
		x /= v.x;
		y /= v.y;
		z /= v.z;
		return *this;
	}
	__device__ Vector3& operator+=(JINGDU a) {
		x += a;
		y += a;
		z += a;
		return *this;
	}
	__device__ Vector3& operator-=(JINGDU a) {
		x -= a;
		y -= a;
		z -= a;
		return *this;
	}
	__device__ Vector3& operator*=(JINGDU a) {
		x *= a;
		y *= a;
		z *= a;
		return *this;
	}
	__device__ Vector3& operator/=(JINGDU a) {
		const JINGDU inv_a = 1.0 / a;
		x *= inv_a;
		y *= inv_a;
		z *= inv_a;
		return *this;
	}

	__device__ JINGDU Dot(const Vector3& v) const {
		return x * v.x + y * v.y + z * v.z;
	}
	__device__ const Vector3 Cross(const Vector3& v) const {
		return {
			y * v.z - z * v.y,
			z * v.x - x * v.z,
			x * v.y - y * v.x
		};
	}

	__device__ bool operator==(const Vector3& rhs) const {
		return x == rhs.x && y == rhs.y && z == rhs.z;
	}
	__device__ bool operator!=(const Vector3& rhs) const {
		return !(*this == rhs);
	}

	__device__ JINGDU& operator[](std::size_t i) {
		return (&x)[i];
	}
	__device__ JINGDU operator[](std::size_t i) const {
		return (&x)[i];
	}

	__device__ std::size_t MinDimension() const {
		return (x < y&& x < z) ? 0u : ((y < z) ? 1u : 2u);
	}
	__device__ std::size_t MaxDimension() const {
		return (x > y && x > z) ? 0u : ((y > z) ? 1u : 2u);
	}
	__device__ JINGDU Min() const {
		return fmin(x, fmin(y, z));
	}
	__device__ JINGDU Max() const {
		return fmax(x, fmax(y, z));
	}

	__device__ JINGDU Norm2_squared() const {
		return x * x + y * y + z * z;
	}

	__device__ JINGDU Norm2() const {
		return std::sqrt(Norm2_squared());
	}

	__device__ void Normalize() {
		const JINGDU a = 1.0 / Norm2();
		x *= a;
		y *= a;
		z *= a;
	}

	//---------------------------------------------------------------------
	// Member Variables
	//---------------------------------------------------------------------
};

//-------------------------------------------------------------------------
// Vector3 Utilities
//-------------------------------------------------------------------------

__device__ inline const Vector3 operator+(JINGDU a, const Vector3& v) {
	return { a + v.x, a + v.y, a + v.z };
}

__device__ inline const Vector3 operator-(JINGDU a, const Vector3& v) {
	return { a - v.x, a - v.y, a - v.z };
}

__device__ inline const Vector3 operator*(JINGDU a, const Vector3& v) {
	return { a * v.x, a * v.y, a * v.z };
}

__device__ inline const Vector3 operator/(JINGDU a, const Vector3& v) {
	return { a / v.x, a / v.y, a / v.z };
}

__device__ inline const Vector3 Sqrt(const Vector3& v) {
	return {
		std::sqrt(v.x),
		std::sqrt(v.y),
		std::sqrt(v.z)
	};
}

__device__ inline const Vector3 Pow(const Vector3& v, JINGDU a) {
	return {
		std::pow(v.x, a),
		std::pow(v.y, a),
		std::pow(v.z, a)
	};
}

__device__ inline const Vector3 Abs(const Vector3& v) {
	return {
		std::abs(v.x),
		std::abs(v.y),
		std::abs(v.z)
	};
}

__device__ inline const Vector3 Min(const Vector3& v1, const Vector3& v2) {
	return {
		fmin(v1.x, v2.x),
		fmin(v1.y, v2.y),
		fmin(v1.z, v2.z)
	};
}

__device__ inline const Vector3 Max(const Vector3& v1, const Vector3& v2) {
	return {
		fmax(v1.x, v2.x),
		fmax(v1.y, v2.y),
		fmax(v1.z, v2.z)
	};
}

__device__ inline const Vector3 Round(const Vector3& v) {
	return {
		std::round(v.x),
		std::round(v.y),
		std::round(v.z)
	};
}

__device__ inline const Vector3 Floor(const Vector3& v) {
	return {
		std::floor(v.x),
		std::floor(v.y),
		std::floor(v.z)
	};
}

__device__ inline const Vector3 Ceil(const Vector3& v) {
	return {
		std::ceil(v.x),
		std::ceil(v.y),
		std::ceil(v.z)
	};
}

__device__ inline const Vector3 Trunc(const Vector3& v) {
	return {
		std::trunc(v.x),
		std::trunc(v.y),
		std::trunc(v.z)
	};
}

__device__ inline const Vector3 Clamp(const Vector3& v,
	JINGDU low = 0.0,
	JINGDU high = 1.0) {
	return {
		Clamp(v.x, low, high),
		Clamp(v.y, low, high),
		Clamp(v.z, low, high) }
	;
}

__device__ inline const Vector3 Lerp(JINGDU a,
	const Vector3& v1,
	const Vector3& v2) {
	return v1 + a * (v2 - v1);
}

template< std::size_t X, std::size_t Y, std::size_t Z >
__device__ inline const Vector3 Permute(const Vector3& v) {
	return { v[X], v[Y], v[Z] };
}

__device__ inline const Vector3 Normalize(const Vector3& v) {
	const JINGDU a = 1.0 / v.Norm2();
	return a * v;
}
